import axios, { AxiosResponse, AxiosRequestConfig, Method, AxiosInstance } from 'axios';
import { RequestConfigInterface } from './service.config';
export default class Axios {
  public noErrAlert = false; // 显示错误弹框[系统错误提示除外=>从接口参数中设置]
  public stringify = false;  // 是否启用对象变字符串[从接口参数中设置]
  public methods: Method[] = ['get', 'post', 'delete', 'put']; // 请求方式(有新的话可以添加)
  public config: RequestConfigInterface; // 初始化配置信息
  constructor(config: RequestConfigInterface ) {
    this.config = config;
  }
  /********** 初始化函数 **********/
  // public init() {}
  /********** code监测层 **********/
  // 监测状态码
  public checkCode(code: any, msg?: string) {
    const { successCodes = [0, 200], errorCodes = {} } = this.config;
    const successCodeCheck = successCodes.some((item => item === code));
    if (successCodeCheck) { return true; } // 如果是成功码直接通过
    if (this.noErrAlert) { return true; } // 接口参数中如果有noErrAlert的话不走config.errorCallback
    // 先判断错误码配置中有没有，有的话配置优先
    if (code in errorCodes) {
      this.config.errorCallback(errorCodes[code]);
    } else {
      // 没有msg的时候抛出错误码
      this.config.errorCallback(msg || code);
    }
  }
  // 检测检查status(针对接口错误时处理)
  public checkStatus(statusCode?: any, msg?: string) {
    if (!statusCode) { return true; } // 如果没有状态码直接通过
    if (this.noErrAlert) { return true; } // 接口参数中如果有noErrAlert的话不走config.errorCallback
    const { statusCodes = {} } = this.config;
    if (statusCode in statusCodes) {
      this.config.errorCallback(statusCodes[statusCode]);
    } else {
      // 没有msg的时候抛出错误码
      this.config.errorCallback(msg || statusCode);
    }
  }
  /********** axios封装核心层 **********/
  // 创建axios实例
  public createAxios() {
    const service = axios.create(this.config); // 生成实例
    this.sendRequest(service); // 发送请求
    this.receiveResponse(service); // 响应请求
    return this.setMethodFun(service, this.config.host); // 封装请求方式
  }
  // 发送请求
  public sendRequest(service: AxiosInstance) {
    service.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 发送请求之前做一些事情 【一般定义加载提示】
        // tslint:disable-next-line: no-console
        console.log('请求接口中...');
        if (this.config.requestBeforeCallback) {
          return this.config.requestBeforeCallback(config);
        } else {
          return config;
        }
      },
      error => {
        return Promise.reject(error); // 挂掉之后怎么处理
      },
    );
  }
  // 响应请求
  public receiveResponse(service: AxiosInstance) {
    service.interceptors.response.use(
      (response: AxiosResponse) => {
        // tslint:disable-next-line: no-console
        console.log('接口请求返回');
        const res = response.data;
        if (this.config.requestAfterCallback) {
          this.config.requestAfterCallback(res);
        }
        if (!this.checkCode(res.code)) {
          return Promise.reject(res);
        }
        return response.data;
      },
      error => {
        // tslint:disable-next-line: no-console
        console.log('接口请求返回错误'); // 处理错误的内容
        const { response, request } = error;
        if (response) {
          this.checkStatus(response.status);
        } else if (request) {
          this.config.errorCallback(error.message);
        }
        return Promise.reject(error);
      },
    );
  }
  // 重置请求方式
  public setMethodFun(service: AxiosInstance, host: RequestConfigInterface['host']) {
    this.methods.forEach((method: Method) => {
      service[method] = (url: string, data: {}, config?: any) => {
        // 检测配置[额外参数移除]
        this.setExtraParams(config, ['stringify', 'noErrAlert']);
        return service({
          url: host + url, // 拼接host
          ...this.getRequestParams(data, method), // 处理数据
          method, // 请求方式
          headers: {
            'Content-Type': 'application/json',
            ...config, // 额外的配置
          },
        });
      };
    });
    return service;
  }
  /********** 数据处理层 **********/
  /**
   * 整理请求参数
   * 思路: 如果是其他请求要让其传递url传参风格,并且使用额外参数来设置弹框显示，或者参数格式化
   * @param data
   * @param method
   */
  public getRequestParams(data: {}, method: Method) {
    if (method === 'get' || this.stringify) {
      return { params: data };
    } else {
      return { data };
    }
  }

  /**
   * 额外参数处理
   * 思路: 通过额外参数来修改是否显示弹框,每次检测接口数据是否有额外参数，如果有则移除额外参数修改全局变量
   * 如果没有则全局变量重置为false,以免全局污染
   * @param data
   * @param extraParams
   */
  public setExtraParams(data: {}, extraParams: string[]) {
    if (typeof data === 'object') {
      for (const extra of extraParams) {
        if (data[extra]) {
          this[extra] = data[extra];
          delete data[extra];
        } else {
          this[extra] = false;
        }
      }
    }
  }
}
